//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//

#include "MeshLoader.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#define _USE_MATH_DEFINES
#include <math.h>

rcMeshLoaderObj::rcMeshLoaderObj() :
	m_scale(fixed64::_1),
	m_verts(0),
	m_tris(0),
	m_normals(0),
	m_vertCount(0),
	m_triCount(0)
{
}

rcMeshLoaderObj::~rcMeshLoaderObj()
{
	delete [] m_verts;
	delete [] m_normals;
	delete [] m_tris;
}

bool read_int(unsigned char* buffer, int length, int& pos, int& v)
{
	if (!buffer || pos < 0 || pos + 4 > length)
		return false;
	v = (int)(buffer[pos] | (buffer[pos+1] << 8) | (buffer[pos + 2] << 16) | (buffer[pos + 3] << 24));
	pos += 4;
	return true;
}

bool read_float(unsigned char* buffer, int length, int& pos, fixed64& v)
{
	int intValue = 0;
	if (!read_int(buffer, length, pos, intValue))
		return false;
	v = fixed64(intValue) * fixed64::EN3;
	return true;
}

bool read_v3(unsigned char* buffer, int length, int& pos, fixed64* v)
{
	if (!read_float(buffer, length, pos, v[0]))
		return false;
	if (!read_float(buffer, length, pos, v[1]))
		return false;
	if (!read_float(buffer, length, pos, v[2]))
		return false;
	return true;
}

bool rcMeshLoaderObj::load(const std::string& filename)
{
	m_filename.clear();
	FILE* fp = fopen(filename.c_str(), "rb");
	if (!fp)
		return false;
	if (fseek(fp, 0, SEEK_END) != 0)
	{
		fclose(fp);
		return false;
	}
	long fileSize = ftell(fp);
	if (fileSize < 0)
	{
		fclose(fp);
		return false;
	}
	if (fseek(fp, 0, SEEK_SET) != 0)
	{
		fclose(fp);
		return false;
	}

	unsigned char* buf = new unsigned char[fileSize];
	if (!buf)
	{
		fclose(fp);
		return false;
	}

	if (fread(buf, fileSize, 1, fp) != 1)
	{
		fclose(fp);
		return false;
	}

	fclose(fp);
	
	if (!load(buf, fileSize))
		return false;

	m_filename = filename;
	return true;
}

bool rcMeshLoaderObj::load(unsigned char* buf, int length)
{
	m_vertCount = 0;
	int pos = 0;
	if (!read_int(buf, length, pos, m_vertCount))
	{
		return false;
	}

	m_verts = new fixed64[m_vertCount * 3];
	for (int i = 0; i < m_vertCount; ++i)
	{
		if (!read_v3(buf, length, pos, m_verts + i * 3))
		{
			return false;
		}
	}

	int tcnt = 0;
	if (!read_int(buf, length, pos, tcnt))
	{
		return false;
	}

	if (tcnt % 3 != 0)
		return false;
	m_triCount = tcnt / 3;

	m_tris = new int[tcnt];
	for (int i = 0; i < tcnt; ++i)
	{
		if (!read_int(buf, length, pos, m_tris[i]))
		{
			return false;
		}
	}

	if (pos != length)
	{
		return false;
	}

	// Calculate normals.
	m_normals = new fixed64[m_triCount * 3];
	for (int i = 0; i < m_triCount * 3; i += 3)
	{
		const fixed64* v0 = &m_verts[m_tris[i] * 3];
		const fixed64* v1 = &m_verts[m_tris[i + 1] * 3];
		const fixed64* v2 = &m_verts[m_tris[i + 2] * 3];
		fixed64 e0[3], e1[3];
		for (int j = 0; j < 3; ++j)
		{
			e0[j] = v1[j] - v0[j];
			e1[j] = v2[j] - v0[j];
		}
		fixed64* n = &m_normals[i];
		n[0] = e0[1] * e1[2] - e0[2] * e1[1];
		n[1] = e0[2] * e1[0] - e0[0] * e1[2];
		n[2] = e0[0] * e1[1] - e0[1] * e1[0];
		fixed64 d = fixed64::sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
		if (d > 0)
		{
			d = fixed64::_1 / d;
			n[0] *= d;
			n[1] *= d;
			n[2] *= d;
		}
	}

	m_scale = fixed64::_1;
	return true;
}
